Skip to content

feat: add external account ownership verification - #806

Open
shreyav wants to merge 8 commits into
mainfrom
shreyav/ownership-verification
Open

feat: add external account ownership verification#806
shreyav wants to merge 8 commits into
mainfrom
shreyav/ownership-verification

Conversation

@shreyav

@shreyav shreyav commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a first-class Ownership Verifications API for verifying that a customer (or the platform) controls a FIRST_PARTY self-custody crypto wallet external account.

Endpoints

  • POST /ownership-verifications — begin a verification for an external account
  • POST /ownership-verifications/{verificationId}/confirm — submit the wallet signature (WALLET_SIGNATURE only)
  • GET /ownership-verifications/{verificationId} — fetch a verification
  • GET /ownership-verifications — list, filterable by externalAccountId and status, with cursor pagination

Resource shape

OwnershipVerification is a oneOf discriminated by method:

  • WALLET_SIGNATURE — returns a messageToSign; completes synchronously via the confirm endpoint
  • LIVENESS — returns a hosted verificationLink plus an optional embed token (only for providers that support direct SDK integration, mirroring the KYC link contract); completes asynchronously (webhooks or polling)

Per-attempt statuses: PENDING, PENDING_REVIEW, VERIFIED, FAILED (failed attempts are retried by starting a new verification).

External account changes

  • New PENDING_OWNERSHIP_VERIFICATION external-account status. It only ever appears for FIRST_PARTY self-custody wallet accounts on platforms subject to EU Travel Rule requirements, so existing integrations are unaffected. Accounts in this status can be used below regulatory transfer thresholds; completing ownership verification moves them to ACTIVE.
  • Expanded the ownershipType field description.

Webhooks

  • New OWNERSHIP_VERIFICATION.PENDING_REVIEW, OWNERSHIP_VERIFICATION.VERIFIED, and OWNERSHIP_VERIFICATION.FAILED events carrying the full verification object.
  • New EXTERNAL_ACCOUNT.STATUS_UPDATED event carrying the full external account object, fired on any status transition (e.g., PENDING_OWNERSHIP_VERIFICATIONACTIVE).

Error codes

  • Renamed WALLET_VERIFICATION_REQUIRED to EXTERNAL_ACCOUNT_VERIFICATION_REQUIRED; removed OWNERSHIP_TYPE_REQUIRED, THIRD_PARTY_TRANSFER_LIMIT_EXCEEDED, and LIGHTNING_TRANSFER_LIMIT_EXCEEDED.

Docs & SDKs

  • Added a shield.svg sidebar icon for the Ownership Verifications group in the Mintlify API reference.
  • Added an ownership_verifications resource block to .stainless/stainless.yml so the endpoints flow into the documented spec and generated SDKs.

Testing

make build bundles cleanly; redocly lint and spectral lint match the pre-existing baseline on main exactly (no new findings).

🤖 Generated with Claude Code

Add verify-ownership and verify-ownership/confirm endpoints for customer
and platform external accounts, supporting wallet-signature and liveness
verification methods. Adds an ownershipVerificationStatus field to
ExternalAccount, EXTERNAL_ACCOUNT.OWNERSHIP_VERIFICATION_* webhook types,
new error codes, and an expanded ownershipType description.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mintlify

mintlify Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
Grid 🟢 Ready View Preview Aug 6, 2026, 4:56 AM

@vercel

vercel Bot commented Aug 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Actions Updated (UTC)
grid-flow-builder Ignored Ignored Preview Aug 11, 2026 3:55pm
grid-wallet-demo Ignored Ignored Preview Aug 11, 2026 3:55pm

Request Review

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

✱ Stainless preview builds for grid

This PR will update the grid SDKs with the following commit messages.

cli

docs(api): clarify ownership-type parameter usage in external accounts

go

feat(api): add ownership verification webhook event and status field to external accounts

kotlin

feat(api): add ownershipVerificationStatus field and webhook events to external accounts

openapi

feat(api): add ownership verification endpoints/webhook to external accounts, update types

php

feat(api): add ownershipVerificationStatus to ExternalAccount, ExternalAccount webhook

python

feat(api): add ownership verification webhook event and status field to external accounts

ruby

feat(api): add ownership verification status field and webhook events to external accounts

typescript

feat(api): add ExternalAccountWebhookEvent, ownershipVerificationStatus to external accounts

Edit this comment to update them. They will appear in their respective SDK's changelogs.

grid-typescript studio · code · diff

Your SDK build had at least one new note diagnostic, which is a regression from the base state.
generate ✅build ✅lint ❗test ✅

npm install https://pkg.stainless.com/s/grid-typescript/3c36e560c6b9cfd862b30f3f965c22de711380fd/dist.tar.gz
New diagnostics (2 note)
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/WalletSignatureOwnershipVerification`
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/LivenessOwnershipVerification`
grid-openapi studio · code · diff

Your SDK build had at least one new note diagnostic, which is a regression from the base state.
generate ⚠️

New diagnostics (2 note)
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/WalletSignatureOwnershipVerification`
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/LivenessOwnershipVerification`
grid-ruby studio · code · diff

Your SDK build had at least one new note diagnostic, which is a regression from the base state.
generate ✅build ✅lint ✅test ✅

New diagnostics (5 note)
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/WalletSignatureOwnershipVerification`
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/LivenessOwnershipVerification`
💡 Ruby/NameShadowedBuiltin: Renamed Ruby method from "method" to "method_" to avoid shadowing a built-in method.
💡 Ruby/NameShadowedBuiltin: Renamed Ruby method from "method" to "method_" to avoid shadowing a built-in method.
💡 Ruby/NameShadowedBuiltin: Renamed Ruby method from "method" to "method_" to avoid shadowing a built-in method.
grid-go studio · code · diff

Your SDK build had at least one new note diagnostic, which is a regression from the base state.
generate ✅build ✅lint ❗test ❗

go get github.com/stainless-sdks/grid-go@feb0e3b93ccf2b3e9e822632f3a4cb6714531ec3
New diagnostics (5 note)
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/WalletSignatureOwnershipVerification`
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/LivenessOwnershipVerification`
💡 Schema/EnumHasOneMember: Confirm intentional use of `enum` with single member.
💡 Schema/EnumHasOneMember: Confirm intentional use of `enum` with single member.
💡 Schema/EnumHasOneMember: Confirm intentional use of `enum` with single member.
grid-kotlin studio · code · diff

Your SDK build had at least one new note diagnostic, which is a regression from the base state.
generate ⚠️build ✅lint ✅test ❗

New diagnostics (5 note)
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/WalletSignatureOwnershipVerification`
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/LivenessOwnershipVerification`
💡 Schema/EnumHasOneMember: Confirm intentional use of `enum` with single member.
💡 Schema/EnumHasOneMember: Confirm intentional use of `enum` with single member.
💡 Schema/EnumHasOneMember: Confirm intentional use of `enum` with single member.
grid-python studio · code · diff

Your SDK build had at least one new note diagnostic, which is a regression from the base state.
generate ✅build ✅lint ❗test ❗

pip install https://pkg.stainless.com/s/grid-python/625891a6ea34769ac9b91ae5a44ff2270d7bb59f/grid-0.0.1-py3-none-any.whl
New diagnostics (2 note)
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/WalletSignatureOwnershipVerification`
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/LivenessOwnershipVerification`
grid-php studio · code · diff

Your SDK build had at least one new note diagnostic, which is a regression from the base state.
generate ✅lint ✅test ✅

New diagnostics (5 note)
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/WalletSignatureOwnershipVerification`
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/LivenessOwnershipVerification`
💡 Schema/EnumHasOneMember: Confirm intentional use of `enum` with single member.
💡 Schema/EnumHasOneMember: Confirm intentional use of `enum` with single member.
💡 Schema/EnumHasOneMember: Confirm intentional use of `enum` with single member.
grid-cli studio · code · diff

generate ⚠️build ❗lint ❗test ⏳

New diagnostics (2 note)
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/WalletSignatureOwnershipVerification`
💡 Model/Recommended: We recommend you use a model for `#/components/schemas/LivenessOwnershipVerification`

⏳ These are partial results; builds are still running.


This comment is auto-generated by GitHub Actions and is automatically kept up to date as you push.
If you push custom code to the preview branch, re-run this workflow to update the comment.
Last updated: 2026-08-11 16:06:08 UTC

…consistency

Payments fire webhooks for their creation-time state (OUTGOING_PAYMENT.PENDING),
so ownership verification does the same: NOT_REQUIRED and REQUIRED join the
async transitions, giving webhook consumers the full lifecycle.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ions resource

Replace the external-account verb endpoints (verify-ownership +
verify-ownership/confirm under /customers and /platform) with a top-level
Ownership Verifications API mirroring the KYC/KYB Verifications pattern:

- POST /ownership-verifications, GET /ownership-verifications (filter by
  externalAccountId/status with cursor pagination),
  GET /ownership-verifications/{verificationId}, and
  POST /ownership-verifications/{verificationId}/confirm
- OwnershipVerification is a oneOf on method: WalletSignatureOwnershipVerification
  (messageToSign, confirmed synchronously) and LivenessOwnershipVerification
  (verificationLink + token, completes asynchronously)
- Drop ExternalAccount.ownershipVerificationStatus; add the
  PENDING_OWNERSHIP_VERIFICATION external-account status for FIRST_PARTY
  self-custody wallets on platforms subject to EU Travel Rule requirements
- Replace EXTERNAL_ACCOUNT.OWNERSHIP_VERIFICATION_* webhooks with
  OWNERSHIP_VERIFICATION.{PENDING_REVIEW,VERIFIED,FAILED} resource webhooks
  and a new EXTERNAL_ACCOUNT.STATUS_UPDATED webhook
- Trim removed error codes and extend AMOUNT_OUT_OF_RANGE description

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…less resource

- Revert AMOUNT_OUT_OF_RANGE description to its original text
- Rename WALLET_VERIFICATION_REQUIRED to EXTERNAL_ACCOUNT_VERIFICATION_REQUIRED
- Document EXTERNAL_ACCOUNT.STATUS_UPDATED as firing on every status
  transition, not only ownership verification
- Register the ownership_verifications resource in .stainless/stainless.yml so
  the endpoints reach the documented spec and SDKs

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
# Conflicts:
#	mintlify/openapi.yaml
#	openapi.yaml
#	openapi/components/schemas/errors/Error400.yaml
@greptile-apps

greptile-apps Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR adds wallet ownership-verification creation, confirmation, retrieval, listing, lifecycle schemas, and related external-account and webhook contracts.

  • Adds WALLET_SIGNATURE and LIVENESS verification methods with lifecycle states.
  • Adds ownership-verification and external-account status webhooks.
  • Registers the new API surface for Stainless SDK generation.
  • Extends external-account status and ownership metadata for regulated self-custody wallets.

Confidence Score: 4/5

The PR appears safe to merge after correcting the non-blocking LIVENESS documentation inconsistency around the optional embed token.

The API surface is coherently wired through source schemas, generated bundles, webhooks, and Stainless, but the create-endpoint description can mislead integrations into assuming every liveness provider returns an embed token.

Files Needing Attention: openapi/paths/ownership_verifications/ownership-verifications.yaml

Important Files Changed

Filename Overview
openapi/paths/ownership_verifications/ownership-verifications.yaml Defines create and list operations; the LIVENESS description incorrectly presents the provider-dependent token as guaranteed.
openapi/paths/ownership_verifications/ownership-verifications_{verificationId}_confirm.yaml Defines synchronous wallet-signature confirmation and its pending, expiry, and method constraints.
openapi/components/schemas/ownership_verifications/OwnershipVerification.yaml Introduces a method-discriminated union for wallet-signature and liveness verification resources.
openapi/components/schemas/ownership_verifications/OwnershipVerificationConfirmRequest.yaml Defines chain-specific signature input, including the signer address and optional Bitcoin signing scheme.
openapi/components/schemas/ownership_verifications/LivenessOwnershipVerification.yaml Models hosted liveness sessions and correctly makes the provider-specific embed token optional.
openapi/components/schemas/webhooks/OwnershipVerificationWebhook.yaml Adds signed webhook payload contracts for pending-review, verified, and failed ownership-verification states.
openapi/components/schemas/external_accounts/ExternalAccountStatus.yaml Adds the regulated PENDING_OWNERSHIP_VERIFICATION account state and documents its transfer restrictions.
.stainless/stainless.yml Registers the ownership-verification resource, models, and four operations for SDK generation.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Create ownership verification] --> B{Method}
  B -->|WALLET_SIGNATURE| C[Receive messageToSign]
  C --> D[Wallet signs message]
  D --> E[Confirm verification]
  B -->|LIVENESS| F[Open verificationLink or use optional token]
  F --> G[Hosted biometric flow]
  E --> H{Result}
  G --> H
  H -->|Pending review| I[Webhook or polling]
  H -->|Verified| J[External account becomes ACTIVE]
  H -->|Failed| K[Start a new verification]
Loading
Prompt To Fix All With AI
### Issue 1
openapi/paths/ownership_verifications/ownership-verifications.yaml:11-12
**Liveness token documented as guaranteed**

For providers without direct SDK integration, `token` is omitted, but this endpoint says every LIVENESS response includes one, leading integrators to initialize an embedded flow without the required token instead of using `verificationLink`.

```suggestion
    - `LIVENESS` — the response includes a `verificationLink` and may include a
      `token` for embedding; the user completes a hosted biometric flow and
```

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "Make liveness verification token optiona..." | Re-trigger Greptile

Comment thread openapi/paths/ownership_verifications/ownership-verifications.yaml Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant